
/**
 * @module svg
 */
(function()
 {
	var SVG_NS = "http://www.w3.org/2000/svg";
	var XLINK_NS = "http://www.w3.org/1999/xlink";
	var MIN_GRIDS = 10;
	var SCALE_MIN = 0.01;
	var SCALE_MAX = 10;
	var DPIN = 1000;	// 1000 dot per inch
	var DPMN = 100;		// 100 dot per mm

	var DOT_PER_UNIT = {
		'in':	1000,
		'mil':	1,
		'mm':	100,
		'cm':	1000
	}
	/**
	 * A drawing board in SVG.
	 * @namespace SVG
	 * @class Board
	 */
	var SVGBoard = G.GClass('SVG', 'Board', G.Base.EventListener);
	SVGBoard.prototype.__init = function(bw, bh, u) {
		this.__Base__EventListener__init();
		this._showGrid = true;
		this._snapGrid = true;
		this._viewGrid = 100;
		this._drawGrid = 100;

		this._mirror = false;

		// Inital Dot per Unit
		this._unit = u || 'mil';
		if (this._unit in DOT_PER_UNIT) {
		} else {
			this._unit = 'mil';
		}
		this._dpu = DOT_PER_UNIT[this._unit];

		this._svg = null;
		this._scale = 0;
		this._scaleRate = 1;
		this._scaleStep = 10;

		this._viewX = 0;
		this._viewY = 0;
		this._viewW = 0;
		this._viewH = 0;

		this._symbols = [];

		// Board Size
		bw = bw * this._dpu;
		bh = bh * this._dpu;

		this._width = bw;
		this._heigth = bh;
		this._boardW = Math.floor((bw + this._viewGrid - 1) / this._viewGrid) * this._viewGrid;
		this._boardH = Math.floor((bh + this._viewGrid - 1) / this._viewGrid) * this._viewGrid;

		this._viewX = 0;
		this._viewY = 0;

		this._selected = {};
		this._objects = {};
		this._objectId = 0;
		this._symbleUsed = {};	// Use count for symbles.
		this._symbles = {};		// 

		this._undoStack = [];
		this._undoIndex = 0;
		this._keycmd = new G.Widget.KeyCommander();
		this._keycmd.enable();

		this._drawContext = null;
		this._idPrefix = 'obj_';
		this._idPrefixLen = 4;
		this.init_draw_context();
	}

	/**
	 * Load short-cut key command list
	 * @method load_hot_key
	 */
	SVGBoard.prototype.load_hot_key = function(cmdlist)
	{
		this._keycmd.load_cmd_list(cmdlist);
	}

	/**
	 * Create SVG element into the container.
	 * @method render
	 * @param container {DOM Element}
	 */
	SVGBoard.prototype.render = function(container) 
	{
		var div = document.createElement('div');
		div.className = 'svgboard';
		container.appendChild(div);
		this._emFrameDiv = div;

		var svg = this._create_element('svg'); 
		svg.setAttribute("width", "100%");
		svg.setAttribute("height", "100%");
		svg.setAttribute('version', '1.1');
		svg.setAttribute('float', 'left');
		svg.setAttribute('display', 'block');
		svg.setAttribute('xmlns', SVG_NS);
		svg.setAttribute('xmlns:xlink', XLINK_NS);
		div.appendChild(svg);
		this._svg = G(svg);

		// Create defs
		var defs = this._create_element('defs');
		this._svg.em.appendChild(defs);
		this._emDefs = defs;

		this._draw_grid();

		// Create background
		var g = this._create_element('g', { width: '100%', height:'100%', 'class': 'background'});
		this._svg.em.appendChild(g);
		this._emBackBox = g;

		// Create drawbox
		g = this._create_element('g', { width: '100%', height:'100%', 'class': 'drawbox'});
		this._emDrawBox = g;
		this._draw_cursor();
		this._svg.em.appendChild(g);

		rect = this._create_element('rect', {'class': 'canvas', width:this._boardW, height: this._boardH});
		g.appendChild(rect);
		this._emCanvas = rect;

		this._update_scale_range();
		this._cursor = { x: 0, y: 0};
		this.zoom_fit();

		G(div).bind("onresize", this._on_resize, this);
		this._svg.bind("onmousedown", this._on_mouse_down, this);
		this._svg.bind("onmouseup", this._on_mouse_up, this);
		this._svg.bind("onmousemove", this._on_mouse_move, this);
		this._svg.bind("onclick", this._on_click, this);

		this._mouseAction = null;
	}

	/**
	 * Set the size of svg board's. the SVGBoard will adjust the scale
	 * range to fit the new size of board.
	 * @method set_size
	 * @param w {integer} width
	 * @param h {integer} height
	 */
	SVGBoard.prototype.set_size = function(w, h)
	{
		this._boardW = w;
		this._boardH = h;
		this._emCanvas.setAttribute('width', this._boardW);
		this._emCanvas.setAttribute('height', this._boardH);

		this._emCursorH.setAttribute('x2', this._boardW);
		this._emCursorV.setAttribute('y2', this._boardH);

		this._update_scale_range();
		this._cursor = { x: 0, y: 0};
		this.zoom_fit();
	}

	SVGBoard.prototype.set_class = function(classname)
	{
		this._svg.em.className = classname;
	}


	/**
	 * Draw the cross line as cursor.
	 * @method _draw_cursor
	 */
	SVGBoard.prototype._draw_cursor = function() 
	{
		var x = this._boardW / 2;
		var y = this._boardH / 2;

		var g = this._create_element('g', {'class': 'cursorbox'});
		this._emCursorH = this._create_element('line', 
				{x1: 0, y1: y, x2: this._boardW, y2: y});
		this._emCursorV = this._create_element('line', 
				{x1: x, y1: 0, x2: x, y2: this._boardH});

		this._emDrawBox.appendChild(this._emCursorH);
		this._emDrawBox.appendChild(this._emCursorV);
		this._emCursorBox = g;
		this._svg.em.appendChild(g);
	}

	/**
	 * Draw the grid box. The grid box include a background rect to show grid
	 * and a cross line as cursor.
	 * @method _draw_grid
	 */
	SVGBoard.prototype._draw_grid = function() 
	{
		if (!this._svg)	return;

		var w = this._viewGrid;
		var ptn = this._create_element('pattern', {id: 'grid', width: w, height: w, patternUnits: 'userSpaceOnUse'});
		var rect = this._create_element('rect', {width:w+0.5, height:w+0.5, stroke:'#030', 'stroke-width':'1', 'shapeRendering': 'crispEdges', fill: 'none'});

		ptn.appendChild(rect);
		this._emGridElement = rect;
		this._emDefs.appendChild(ptn);


		var g = this._create_element('g', {'class':'gridbox', width:'100%', height:'100%'});
		var b = this._create_element('rect', { width: '100%', height:'100%'});
		g.appendChild(b);
		this._svg.em.appendChild(g);
		this._emGridRect = b;
		this._emGridBox = g;
	}


	/**
	 * This funciton will calculate the MIN, MAX and FIT scale of the board.
	 * The minimum of scale to make sure the whole draw board can be fit into 
	 * window, but the value must be in the range of 0.001 -- 1.</br>
	 * The fit scale will make the whole board displayed into the view area.
	 *
	 * When zoom in/out, if the scale was set into the range of 0.1 -- 1, 
	 * it must be regulate to multiple of 0.1 to avoid the deviation of grid.
	 * The size of grid is always multiple of 10.
	 *
	 * if the scale was set to smaller than 0.1, the grid will dispared.
	 *
	 * @method _update_scale_range
	 */
	SVGBoard.prototype._update_scale_range = function() 
	{
		//var cw = this._svg.em.clientWidth;
		//var ch = this._svg.em.clientHeight;
		var cw = this._emFrameDiv.clientWidth;
		var ch = this._emFrameDiv.clientHeight;
		if (ch < 200)	ch = 200;
		if (cw < 200)	cw = 200;

		var scaleW = cw / this._boardW;
		var scaleH = ch / this._boardH;
		var scale =(scaleW < scaleH)?	scaleW : scaleH;

		// We can always scale to 1:1;
		this._scaleFit = (scale > 1)? 1 : scale;
		if (scale < 0.1) {
			this._scaleFit = scale;
			this._scaleMin = scale;
		}

		if (scale < SCALE_MIN) {
			scale = SCALE_MIN;
			this._scaleMin = scale;
			this._scaleFit = scale;
		} else if (scale < 0.1) {
			this._scaleMin = scale;
			this._scaleFit = scale;
		} else if (scale < 1) {
			scale = Math.floor(scale * 10) / 10;
			this._scaleMin = scale;
			this._scaleFit = scale;
		} else {
			this._scaleMin = 1;
			this._scaleFit = 1;
		}
		this._scaleMax = SCALE_MAX;

		var rate = Math.log(SCALE_MAX / scale) / Math.log(this._scaleStep);
		this._scaleRate = rate;
	}

	/**
	 * Set how many steps to zoom the scale from minimum to maximum.
	 * The default step is 10.
	 * @method set_scale_step
	 * @param <integer> step
	 */
	SVGBoard.prototype.set_scale_step = function(step)
	{
		if (step < 2)	step = 2;
		this._scaleStep = step;
		this._update_scale_range();
	}

	/**
	 * When the size of svg container was changed,  we need calculate the
	 * scale range again.
	 * @method _on_resize
	 */
	SVGBoard.prototype._on_resize = function(evt) 
	{
		this._update_scale_range();
		if (this._scale < this._scaleMin) {
			this.zoom_fit();
		}
	}


	SVGBoard.prototype._create_element = function(typ, attr) 
	{
		var attr = attr || null;
		if (typeof(typ) != 'string') {
			console.log(typ);
		}
		if (document.createElementNS) {
			var o = document.createElementNS(SVG_NS, typ);
		} else {
			var o = document.createElement(typ);
		}
		if (attr) {
			for (var i in attr) {
				o.setAttribute(i, attr[i]);
			}
		}
		return o;
	}


	/**
	 * Set the grid size. the grid size must be a multiply of 10.
	 * to avoid the deviation when zoom in/out.
	 * @method set_grid
	 */
	SVGBoard.prototype.set_grid = function(grid) 
	{
		var grid = Math.floor(grid / 10) * 10;
		if (grid < 10)	grid = 10;

		this._viewGrid = grid;

		this._emGridElement.setAttribute('width', grid);
		this._emGridElement.setAttribute('height', grid);
	}

	/**
	 * Set the grid on/off
	 * @method view_grid
	 * @param on {boolean}
	 */
	SVGBoard.prototype.view_grid = function(on)
	{
		if (on) {
			this._emGridElement.removeAttribute('display');
		} else {
			this._emGridElement.setAttribute('display', 'none');
		}
	}

	/**
	 * Fix the value of coordinate to align on grid.
	 * @method _snap_to_grid
	 */
	SVGBoard.prototype._snap_to_grid = function(x, y, grid) 
	{
		var grid = grid || this._drawGrid;
		var x  = Math.round(x / grid) * grid;
		var y  = Math.round(y / grid) * grid;
		return {x: x, y: y};
	}


	/**
	 * Get the point of mouse event in board's coordinate system.
	 * @method _map_mouse_matrix
	 * @param evt {mouse event}
	 * @return {x,y} the X,Y value of coordinate.
	 */
	SVGBoard.prototype._map_mouse_matrix = function(evt)
	{
		var CTM = this._emDrawBox.getCTM();
		var p = this._svg.em.createSVGPoint();
		p.x = evt.offsetX || evt.layerX;
		p.y = evt.offsetY || evt.layerY;
		//console.log('evt: ' + p.x + ',' + p.y);
		var p2 = p.matrixTransform(CTM.inverse());
		
		p = this._snap_to_grid(p2.x, p2.y);
		//console.log('mouse: ' + p.x + ',' + p.y);
		return p;
	}


	/**
	 * Mirror the board.
	 * @method mirror
	 * @param mirror {boolean}
	 */
	SVGBoard.prototype.mirror = function(mirror)
	{
		if (mirror != this._mirror) {
			this._mirror = mirror;
			this._update_matrix();
		}
	}

	/**
	 * Update the transform matrix according the scale, offset and mirror 
	 * status of the board.
	 * @method _update_matrix
	 */
	SVGBoard.prototype._update_matrix = function()
	{
		// Xn = X * m0 + Y * m2 + m4 
		// Yn = X * m1 + Y * m3 + m5
		var m = [];
		m[1] = 0;
		m[2] = 0;
		m[3] = this._scale;

		var cx = this._cursor.x * this._scale;
		var cy = this._cursor.y * this._scale;

		var g = this._viewGrid * this._scale;
		//var w = Math.floor(this._svg.em.clientWidth / g / 2) * g;
		//var h = Math.floor(this._svg.em.clientHeight / g / 2) * g;
		var w = Math.floor(this._emFrameDiv.clientWidth / g / 2) * g;
		var h = Math.floor(this._emFrameDiv.clientHeight / g / 2) * g;

		m[5] = h - cy;	// Offset Y
		if (this._mirror) {
			m[0]  = Math.floor(-1 * this._scale * 1000) / 1000;
			m[4] = w + cx;
		} else {
			m[0] = this._scale;
			m[4] = w - cx;
		}

		var matrix = 'matrix(' + m.join(' ') + ')';
		this._emDrawBox.setAttribute('transform', matrix);
		this._emBackBox.setAttribute('transform', matrix);
		console.log(matrix);
	}


	/**
	 * Move and scale the view window.
	 * @method set_view
	 * @param scale {float}
	 * @param center {integer} the center of the view area, the value
	 * will be fixed to align to grid.
	 */
	SVGBoard.prototype.set_view = function(scale, center)
	{
		// Recalculate the view box size if scale changed.
		var update = false;

		if (scale < this._scaleMin) {
			scale = this._scaleMin;
		} else if (scale > this._scaleMax) {
			scale = this._scaleMax;
		}

		if (center.x < 0)	center.x = 0;
		if (center.y < 0)	center.y = 0;
		if (center.x >= this._boardW)	center.x = this._boardW;
		if (center.y >= this._boardH)	center.y = this._boardH;

		cp = this._snap_to_grid(center.x, center.y);	// Center point

		if (scale != this._scale) 
		{
			this._scale = scale;
			update = true;

			// Resize the grid box and cursor box the make it full fill the window.
			var i = '100%'
			if (scale < 1) {
				i = Math.round(100 / this._scale) + '%';
			}
			this._emGridRect.setAttribute('width', i);
			this._emGridRect.setAttribute('height', i);

			var tr = 'scale(' + this._scale + ')';
			this._emGridBox.setAttribute('transform', tr);
			this._emCursorBox.setAttribute('transform', tr);
		}

		if ((cp.x != this._cursor.x) || (cp.y != this._cursor.y) || update) {
			this._cursor.x = cp.x;
			this._cursor.y = cp.y;
			this._update_matrix();
		} else {
			return;
		}
		this.fire('onscalechanged', {scale: this._scale, x: this._viewX, y: this._viewY});
	}


	/**
	 * Zoom the board to fit the view area.
	 * @method zoom_fit
	 */

	SVGBoard.prototype.zoom_fit= function() 
	{
		var p = {x: this._boardW / 2, y: this._boardH / 2};
		this.set_view(this._scaleFit, p);
	}

	/**
	 * Make sure the scale was a multiple of 0.1 if it's greater than 0.1
	 * @method _fix_scale
	 * @param scale {float}
	 * @return {float}
	 */
	SVGBoard.prototype._fix_scale = function(scale) 
	{
		if (scale >= 0.1) {
			scale = Math.floor(scale * 10) / 10;
		}
		return scale;
	}

	SVGBoard.prototype.zoom_in = function() 
	{
		var scale = this._fix_scale(this._scale * this._scaleRate);

		this.set_view(scale, this._cursor);
	}

	SVGBoard.prototype.zoom_out = function() 
	{
		var scale = this._fix_scale(this._scale / this._scaleRate);

		this.set_view(scale, this._cursor);
	}

	/* ======================================================================
	 * Edit commands
	 * ======================================================================
	 */

	/**
	 * Append an element into the draw box.
	 *
	 * @method add_element
	 * @param shp {string} the tag name of the element
	 * @param attr {object} the attributes of the element
	 * @param p {SVG Element} the father of the element, default to draw box
	 * @return {SVG Element}
	 */
	SVGBoard.prototype.add_element = function(shp, attr, p) 
	{
		var p = p || this._emDrawBox;
		var em = this._create_element(shp, attr);
		p.appendChild(em);
		return em;
	}

	/**
	 * Remove an element.
	 * @method remove_element
	 * @param em {SVG Element}
	 */
	SVGBoard.prototype.remove_element = function(em)
	{
		var p = em.parentNode;
		if (p) {
			p.removeChild(em);
		}
	}


	/**
	 * Append a SVG element into background.
	 * @method add_background
	 * @param shp {string} the tag name of the element
	 * @param attr {object} the attributes of the element
	 * @param p {SVG Element} the father of the element, default to background box.
	 */
	SVGBoard.prototype.add_background = function(shp, attr, p)
	{
		var p = p || this._emBackBox;
		var em = this._create_element(shp, attr);
		this._emBackBox.appendChild(em);
	}

	/**
	 * Add an element into defs with a name.
	 * @method def_symbol
	 * @param name {string} the name of the defined element.
	 * @param tag {string} the tag name of the element default to 'g'
	 * @param attr {string} the attribute of the element
	 * @return {SVG element}
	 */
	SVGBoard.prototype.def_symbol = function(name, tag, attr) 
	{
		var tag = tag || 'g';
		var attr = attr || {};
		attr.id = name;
		var em = this._create_element(tag, attr);
		this._emDefs.appendChild(em);

		return em;
	}

	/**
	 * Add a use to the predefined symbol.
	 * @method add_use
	 * @param symName {string} the name of symbol to use.
	 * @param attr {string} the attribute of the element;
	 * @param parent {SVG Element} the parent node of the element.
	 * @return {SVG Element}
	 */
	SVGBoard.prototype.add_use = function(symName, attr, parent)
	{
		var em = this._create_element('use');
		var parent = parent || this._emDrawBox;
		em.setAttributeNS(XLINK_NS, 'xlink:href', '#' + symName);
		for (var a in attr) {
			em.setAttribute(a, attr[a]);
		}
		parent.appendChild(em);
		return em;
	}

	SVGBoard.prototype._json_svg_element = function(em)
	{
		var o = {'tag': em.tagName};
		o.attr = em.attributes;

		for (var i in this.childNodes) {
			o.childs[i] = this._json_svg_element(em.childNodes[i]);
		}
		return o;
	}


	SVGBoard.prototype.get_json = function()
	{
		var o = {'tag': 'g'};
		for (var i = 1; i < this._emDrawBox.childNodes.length; i++) {
			o.childs[i-1] = this._json_svg_element(this._emDrawBox.childNodes[i]);
		}
		return 0;
	}


	/**
	 * Move the cursor in the coordinate system of board.
	 * @method move_cursor
	 * @param p {point} The x/y value of the target position.
	 */
	SVGBoard.prototype.move_cursor = function(p)
	{
		if ((this._cursor.x != p.x) || (this._cursor.y != p.y)) {
			this.fire('oncursormove', p);
			var x = p.x;
			var y = p.y;
			if (x > this._boardW)	x = this._boardW;
			if (x < 0)	x = 0;
			if (y > this._boardH)	y = this._boardH;
			if (y < 0)	y = 0;

			this._emCursorV.setAttribute('x1', x+0.5);
			this._emCursorV.setAttribute('x2', x+0.5);
			this._emCursorH.setAttribute('y1', y+0.5);
			this._emCursorH.setAttribute('y2', y+0.5);
		} 
		this._cursor = p;
	}

	/*
	 * Mouse Event handlers
	 */
	SVGBoard.prototype._call_mouse_action = function(evname, evt)
	{
		var p = this._map_mouse_matrix(evt);
		this.move_cursor(p);

		p.srcObject = this.find_object(evt.srcElement);

		if (!this._mouseAction)		return;

		var act = this._mouseAction;
		this._drawContext.pb = {x: p.x, y: p.y};

		if (typeof(act[evname]) == 'function') {
			act[evname].call(act.obj, p, evt);
		}
	}


	SVGBoard.prototype._on_mouse_down = function(evt) 
	{
		this._call_mouse_action('onmousedown', evt);
	}
	SVGBoard.prototype._on_mouse_move = function(evt) 
	{
		this._call_mouse_action('onmousemove', evt);

		var ctx = this._drawContext;
		if (ctx.drag) {
			if (typeof(ctx.drag.drag) == 'function') {
				ctx.drag.drag(ctx);
			}
		}
	}

	SVGBoard.prototype._on_mouse_up = function(evt) 
	{
		this._call_mouse_action('onmouseup', evt);
	}

	SVGBoard.prototype._on_click = function(evt) 
	{
		this._call_mouse_action('onclick', evt);
	}

	SVGBoard.prototype.set_mouse_action = function(action)
	{
		if (this._mouseAction && (typeof(this._mouseAction.deinit) == 'function')) {
			this._mouseAction.deinit.call(this._mouseAction.obj);
		}
		this._mouseAction = action;
		if (this._mouseAction && (typeof(this._mouseAction.init) == 'function')) {
			this._mouseAction.init.call(this._mouseAction.obj);
		}
		if (action) {
			this.fire('ondrawmodechanged', action.name);
		} else {
			this.fire('ondrawmodechanged', '');
		}
	}


	SVGBoard.prototype.clear = function()
	{
		var len = this._drawBox.childNodes.length;
	}

	SVGBoard.prototype.init_draw_context = function()
	{
		if (this._drawContext) {
			if (this._drawContext.draw) {
				this._drawContext.draw.cancel_fly();
			}
		}
		this._drawContext = {
			pa: null,			// Start point
			pb: null,			// Second point
			container: null,	// Container element
			r: 0,				// Rotate angle 0 -- 360
			draw: null,			// Object in drawing
			drag: null			// Object in drag
		}
	}

	SVGBoard.prototype.set_draw_context = function(attr, value)
	{
		this._drawContext[attr] = value;
	}


	/**
	 * Make the current drawing object fly with cursor
	 * @method fly
	 */
	SVGBoard.prototype.fly = function(obj)
	{
		var obj = obj || null;
		if (obj) {
			obj.render(this._drawContext);
		} else {
			if (this._drawContext.draw) {
				this._drawContext.draw.fly(this._drawContext);
			}
		}
	}

	SVGBoard.prototype.get_draw_context= function()
	{
		return this._drawContext;
	}

	SVGBoard.prototype.in_drawing = function()
	{
		return this._drawContext.draw;
	}

	SVGBoard.prototype.draw_end = function()
	{
		if (this._drawContext.draw) {
			this._drawContext.draw.cancel_fly();
		}
		this._drawContext.draw = null;
	}

	SVGBoard.prototype.draw_finish = function()
	{
		var obj = this._drawContext.draw;
		this._drawContext.draw = null;
		return obj;
	}

	SVGBoard.prototype.rotate = function()
	{
		if (!this._drawContext)	return;
		var r = this._drawContext.r + 90;
		if (r >= 360)	r = 0;
		this._drawContext.r  = r;

		if (this._drawContext.draw) {
			this._drawContext.draw.rotate(this._drawContext);
		} else if (this._drawContext.select) {
			// todo: rotate selected object
		}
	}

	SVGBoard.prototype.end_draw = function()
	{
		if (this._drawContext.draw) {
			this._drawContext.draw.cancel_fly();
		}
	}


	SVGBoard.prototype.start_drag = function(pt)
	{
		this._drawContext.drag = pt.srcObject;
		this._drawContext.pa = {x: pt.x, y: pt.y};
	}

	SVGBoard.prototype.end_drag = function()
	{
		this._drawContext.drag = null;
	}


	/* ======================================================================
	 * Object managment
	 * ======================================================================
	 */
	SVGBoard.prototype.set_obj_prefix = function(prefix)
	{
		this._objPrefix = prefix;
	}

	/**
	 * Set prefix for object's id.
	 * @method set_id_prefix
	 * @param prefix {string}
	 */
	SVGBoard.prototype.set_id_prefix = function(prefix)
	{
		this._idPrefix = prefix;
		this._idPrefixLen = prefix.length;
	}


	/**
	 * Find the SVGObject that the element belong to.
	 * @method _find_object
	 */
	SVGBoard.prototype.find_object = function(element)
	{
		for (var p = (element.correspondingUseElement || element); p; p = p.parentNode) {
			var id = p.id || '';
			if (id.substring(0, this._idPrefixLen) == this._idPrefix) {
				return this._objects[id] || null;
			}
		}
		return null;
	}


	/**
	 * Add an object to board. assign a id to it.
	 * @method add_object
	 * @param obj {SvgObject}
	 */
	SVGBoard.prototype.add_object = function(obj)
	{
		var id = G.get_uuid(this._idPrefix);
		obj.id = id;
		obj.element.setAttribute('id', id);
		this._objects[id] = obj;
		return id;
	} 

	/**
	 * Remove the object with the given id.
	 * @method remove_object
	 * @param id {string} 
	 */
	SVGBoard.prototype.remove_object = function(id)
	{
		if (id in this._objects) {
			var obj = this._objects[id];
			this.remove_element(obj.element);
			delete this._objects[id];
		}
		if (id in this._selected) {
			delete this._selected[id];
		}
	}

	SVGBoard.prototype.remove_selected = function()
	{
		for (var id in this._selected) {
			this.remove_element(this._selected[id].element);
			delete this._selected[id];
			delete this._objects[id];
		}
	}

	SVGBoard.prototype.is_selected = function(id)
	{
		if (id in this._selected)	return true;
		return false;
	}

	SVGBoard.prototype.select = function(id)
	{
		if (id in this._objects) {
			var o = this._objects[id];
			this._selected[id] = o;
			o.element.setAttribute('class', 'selected');

			this._lastSelected = o;
		}
	}


	SVGBoard.prototype.deselect = function(id)
	{
		if (id in this._selected) {
			var o = this._selected[id];
			o.element.removeAttribute('class');
			delete this._selected[id];
		}
	}

	SVGBoard.prototype.deselect_all = function()
	{
		for (var id in this._selected) {
			var o = this._selected[id];
			o.element.removeAttribute('class');
		}
		this._selected = {};
	}



	/**
	 *
	 * @calss SVGObject
	 */
	SVGObject = G.GClass('SVG', 'Obj');
	SVGObject.prototype.__init = function(board)
	{
		this.board = board;
		this.element = null;
	}

	SVGObject.prototype.cancel_fly = function()
	{
		if (this.element) {
			this.board.remove_element(this.element);
		}
	}


	SVGObject.prototype._render_root = function(tag, attr, context)
	{
		for (var a in context.attr) {
			attr[a] = context.attr[a];
		}
		this.element= this.board.add_element(tag, attr, context.container);
		context.draw = this;
		return this.element;
	}

	SVGObject.prototype.rotate = function(context){}


	SVGObjLine = G.GClass('SVG', 'ObjLine', G.SVG.Obj);
	SVGObjLine.prototype.__init = function(board)
	{
		this.__SVG___Obj__init(board);
	}

	SVGObjLine.prototype.render = function(context)
	{
		var context = context || this.board._drawContext;
		var pa = context.pa;
		var pb = context.pb || context.pa;
		var attr = {'x1': pa.x, 'y1': pa.y, 'x2': pb.x, 'y2': pb.y};
		return this._render_root('line', attr, context);
	}

	SVGObjLine.prototype.fly = function(context)
	{
		if (!this.element)	return;

		this.element.setAttribute('x2', pb.x);
		this.element.setAttribute('y2', pb.y);
	}

	SVGObjRect = G.GClass('SVG', 'ObjRect', G.SVG.Obj);
	SVGObjRect.prototype.__init = function(board)
	{
		this.__SVG___Obj__init(board);
	}

	SVGObjRect.prototype.render = function(context)
	{
		var pa = context.pa;
		var pb = context.pb || context.pa;
		var attr = {'x': pa.x, 'y': pa.y, 
					'width': pb.x - pa.x, 
					'height': pb.y - pa.y
		};
		return this._render_root('rect', attr, context);
	}

	SVGObjRect.prototype.fly = function(context)
	{
		if (!this.element)	return;

		this.element.setAttribute('width', pb.x - pa.x);
		this.element.setAttribute('height', pb.y - pa.y);
	}

	SVGObjText = G.GClass('SVG', 'ObjText', G.SVG.Obj);
	SVGObjText.prototype.__init = function(board, txt)
	{
		this.__SVG__Obj__init(board);
		this.text = txt;
	}
	SVGObjText.prototype.render = function(context)
	{
		var pa = context.pa;
		var attr = {'x': pa.x, 'y': pa.y};
		var txt = this._render_root('text', attr, context);
		var tn = document.createTextNode(this.txt);
		txt.appendChild(tn);
		this.x = context.pa.x;
		this.y = context.pa.y;
		this.layer = context.layer;
		return txt;
	}

	SVGObjText.prototype.fly = function(context)
	{
		this.x = context.pb.x;
		this.y = context.pb.y;
		this.element.setAttribute('x', this.x);
		this.element.setAttribute('y', this.y);
	}

	SVGObjText.prototype.to_json = function()
	{
		return {
			type: 'text',
			layer: this.layer,
			text: this.text,
			x: this.x,
			y: this.y
		}
	}


	/**
	 * Add SVGBoard creater to DOM.Element's prototype.
	 *
	 */
	G.DOM.Element.prototype.SVGBoard = function(w, h, u) {
		var board = new SVGBoard(w, h, u);
		board.render(this.em);
		return board;
	}
 })();

